//! 本协议用于从 ConsoleIn 设备中获取输入，也是 ConsoleIn 设备至少需要实现的协议。
use core::ffi::c_void;

use crate::{
    guid_struct,
    {services::boot::event::EfiEvent, EfiGuid, EfiStatus},
};

pub type Reset = extern "efiapi" fn(
    this: *mut EfiSimpleTextInputProtocol,
    extended_verification: bool,
) -> EfiStatus;

pub type EfiInputReadKey =
    extern "efiapi" fn(this: *mut EfiSimpleTextInputProtocol, key: *mut EfiInputKey) -> EfiStatus;

#[repr(C)]
pub struct EfiInputKey {
    /// 相关扫描码见 [`ScanCode`](super::scan_code)
    pub scan_code: u16,
    /// unicode_char 是可打印字符或为 0（此时表示非打印字符，如控制按键，功能按键等等）
    pub unicode_char: u16,
}

#[repr(C)]
pub struct EfiSimpleTextInputProtocol {
    /// 复位输入设备（硬件上）
    /// 复位操作会清空驻留在内存中的任何（用于缓冲按键数据的）输入队列，并将输入流置于已知的空状态。
    ///
    /// 作为初始化的一部分，固件或设备会快速的进行设备验证，以便判断设备是否工作。
    ///
    /// ### 参数
    /// extended_verification: 用来指示驱动在复位期间是否执行更多的验证操作。
    ///
    /// ### 返回的状态码
    /// + EFI_SUCCESS: 设备成功复位
    /// + EFI_DEVICE_ERROR: 设备工作不正常，并且不能够被复位。
    pub rest: Reset,
    /// 从输入设备中读取下一个按键。
    ///
    /// ### 参数
    /// + this: 指向 [`EfiSimpleTextInputProtocol`] 实列的指针。
    /// + key: 一个指向按键信息缓冲区的指针。读取的按键信息将被存放在 key 所指向的内存位置。
    ///
    /// ### 返回的状态码
    /// + EFI_SUCCESS: 按键信息被成功返回。
    /// + EFI_NOT_READY: 尚无可获取的按键数据。
    /// + EFI_DEVICE_ERROR: 由于硬件错误而无法返回按键信息。
    /// + EFI_UNSUPPORTED: 设备不支持读取按键数据。
    pub read_key_storke: EfiInputReadKey,
    /// wait_for_key 是一个用于 `EFI_BOOT_SERVICES.WaitForEvent()` 的事件，用于等待一个按键。
    pub wait_for_key: EfiEvent,
}

impl EfiSimpleTextInputProtocol {
    pub const GUID: EfiGuid =
        guid_struct! {0x387477c1,0x69c7,0x11d2, {0x8e,0x39,0x00,0xa0,0xc9,0x69,0x72,0x3b}};
}

pub type EfiInputResetEx = extern "efiapi" fn(
    this: *mut EfiSimpleTextInputExProtocol,
    extended_verification: bool,
) -> EfiStatus;

pub type EfiInputReadKeyEx = extern "efiapi" fn(
    this: *mut EfiSimpleTextInputExProtocol,
    key_data: *mut EfiKeyData,
) -> EfiStatus;
pub struct EfiKeyData {
    pub key: EfiInputKey,
    pub key_state: EfiKeyState,
}
pub type EfiKeyToggleState = u8;
pub struct EfiKeyState {
    pub key_shift_state: u32,
    pub key_toggle_state: EfiKeyToggleState,
}

pub mod key_shift_state {
    pub const EFI_SHIFT_STATE_VALID: u32 = 0x80000000;
    pub const EFI_RIGHT_SHIFT_PRESSED: u32 = 0x00000001;
    pub const EFI_LEFT_SHIFT_PRESSED: u32 = 0x00000002;
    pub const EFI_RIGHT_CONTROL_PRESSED: u32 = 0x00000004;
    pub const EFI_LEFT_CONTROL_PRESSED: u32 = 0x00000008;
    pub const EFI_RIGHT_ALT_PRESSED: u32 = 0x00000010;
    pub const EFI_LEFT_ALT_PRESSED: u32 = 0x00000020;
    pub const EFI_RIGHT_LOGO_PRESSED: u32 = 0x00000040;
    pub const EFI_LEFT_LOGO_PRESSED: u32 = 0x00000080;
    pub const EFI_MENU_KEY_PRESSED: u32 = 0x00000100;
    pub const EFI_SYS_REQ_PRESSED: u32 = 0x00000200;
}

pub mod key_toggle_state {
    pub const EFI_TOGGLE_STATE_VALID: u32 = 0x80;
    pub const EFI_KEY_STATE_EXPOSED: u32 = 0x40;
    pub const EFI_SCROLL_LOCK_ACTIVE: u32 = 0x01;
    pub const EFI_NUM_LOCK_ACTIVE: u32 = 0x02;
    pub const EFI_CAPS_LOCK_ACTIVE: u32 = 0x04;
}

pub type EfiSetState = extern "efiapi" fn(
    this: *mut EfiSimpleTextInputExProtocol,
    key_toggle_state: *mut EfiKeyToggleState,
) -> EfiStatus;

pub type EfiKeyNotifyFunction = extern "efiapi" fn(key_data: *mut EfiKeyData) -> EfiStatus;

pub type EfiRegisterKeystrokeNotify = extern "efiapi" fn(
    this: *mut EfiSimpleTextInputExProtocol,
    key_data: *mut EfiKeyData,
    key_notification_function: EfiKeyNotifyFunction,
    notify_handle: *mut *mut c_void,
) -> EfiStatus;

pub type EfiUnregisterKeystrokeNotify = extern "efiapi" fn(
    this: *mut EfiSimpleTextInputExProtocol,
    notification_handle: c_void,
) -> EfiStatus;

pub struct EfiSimpleTextInputExProtocol {
    pub reset: EfiInputResetEx,
    pub read_key_stroke_ex: EfiInputReadKeyEx,
    pub wait_for_key: EfiEvent,
    pub set_state: EfiSetState,
    pub register_key_notify: EfiRegisterKeystrokeNotify,
    pub unregister_key_notify: EfiUnregisterKeystrokeNotify,
}

impl EfiSimpleTextInputExProtocol {
    pub const GUID: EfiGuid = guid_struct! {0xdd9e7534, 0x7762, 0x4698,  {0x8c, 0x14, 0xf5, 0x85, 0x17, 0xa6, 0x25, 0xaa}};
}
